package king.dominic.jlibrary.util;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.text.TextUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by DominicKing on 2017/8/30.
 * FragmentFactory用于在FragmentParent中创建Fragment，并且实现Fragment各种情况下的回收再使用
 *
 * @see FragmentParent
 * @see FragmentSaver
 */

public class FragmentFactory {
    private static final String TAG = FragmentFactory.class.getSimpleName();

    /**
     * @see FragmentFactory#obtain(FragmentParent, Class, Bundle)
     */
    public static <F extends Fragment> F obtain(FragmentParent parent, Class<F> clazz) {
        return obtain(parent, clazz, (Bundle) null);
    }

    public static <F extends Fragment> F replace(FragmentParent parent, int resId, Class<F> clazz) {
        final F f = obtain(parent, clazz);
        parent.gFragmentManager().beginTransaction().replace(resId, f).commit();
        return f;
    }

    /**
     * Kotlin 使用 mapOf 快速填装
     */
    public static <F extends Fragment> F obtain(FragmentParent parent, Class<F> clazz, Map<String, Object> args) {
        Bundle bundle = new Bundle();
        if (args != null && !args.isEmpty()) {
            Set<String> keys = args.keySet();
            for (String key : keys) {
                Object obj = args.get(key);
                if (obj instanceof Integer) {
                    bundle.putInt(key, (Integer) obj);
                    continue;
                }
                if (obj instanceof Float) {
                    bundle.putFloat(key, (Float) obj);
                    continue;
                }
                if (obj instanceof String) {
                    bundle.putString(key, (String) obj);
                    continue;
                }
                if (obj instanceof Byte) {
                    bundle.putByte(key, (Byte) obj);
                    continue;
                }
                if (obj instanceof Character) {
                    bundle.putChar(key, (Character) obj);
                    continue;
                }
                if (obj instanceof Bundle) {
                    bundle.putAll(bundle);
                    continue;
                }
                if (obj instanceof Serializable)
                    bundle.putSerializable(key, (Serializable) obj);
            }
        }
        return obtain(parent, clazz, bundle);
    }

    /**
     * 获取Fragment
     *
     * @param parent {@link FragmentParent}
     * @param clazz  Fragment的Class
     * @param args   要传给Fragment的参数,见：{@link Fragment#setArguments(Bundle)}
     * @param <F>    类型为必须继承自{@link Fragment}
     * @return Fragment
     */
    @SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
    public static <F extends Fragment> F obtain(FragmentParent parent, Class<F> clazz, Bundle args) {
        final String className = clazz.getName();
        FragmentManager fm = parent.gFragmentManager();
        String tag = getFragmentTAG(parent, className);
        Fragment f = null;
        if (!TextUtils.isEmpty(tag)) {
            f = fm.findFragmentByTag(tag);
        }

        if (f == null) {
            try {
                f = clazz.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        if (args != null && f != null)
            f.setArguments(args);

        return (F) f;
    }

    @SuppressWarnings("unchecked")
    private static String getFragmentTAG(FragmentParent parent, String f) {
        HashMap<String, Object> fs = parent.getFragments();
        if (fs == null || fs.isEmpty()) return null;
        Object obj = fs.get(f);
        if (obj == null) return null;
        if (obj instanceof String) {
            return (String) obj;
        }
        if (obj instanceof ArrayList) {
            Object fra = ((ArrayList) obj).remove(0);
            ((ArrayList) obj).add(fra);
            return (String) fra;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static void saveFragmentTAG(HashMap<String, Object> fs, String tag, String className) {
        if (fs.containsKey(className)) {
            Object obj = fs.remove(className);
            if (obj instanceof String) {
                ArrayList ll = new ArrayList();
                ll.add(obj);
                ll.add(tag);
                fs.put(className, ll);
            } else if (obj instanceof ArrayList) {
                ((ArrayList) obj).add(tag);
                fs.put(className, obj);
            }
        } else {
            fs.put(className, tag);
        }
    }

    /**
     * 从FragmentParent获取Fragments,保存到outState中
     * 在本页面的onSaveInstanceState中调用此方法。
     * 重新生成页面后，在onCreate(OnCreateView等)中调用parseSavedInstanceState，
     * 得到保存的Fragments。
     *
     * @param parent   {@link FragmentParent}
     * @param outState {@link Bundle}
     * @see FragmentFactory#parseSavedInstanceState(FragmentParent, Bundle)
     */
    public static void onSaveInstanceState(FragmentParent parent, Bundle outState) {
        List<Fragment> fragments = parent.gFragmentManager().getFragments();

        if (fragments.isEmpty()) return;

        HashMap<String, Object> fs = parent.getFragments();
        if (fs == null) fs = new HashMap<>();
        fs.clear();

        for (Fragment f : fragments) {
            if(f==null)continue;
            String className = f.getClass().getName();
            String tag = f.getTag();
            saveFragmentTAG(fs, tag, className);
        }
        outState.putSerializable("OriginActivityFragment_FF", fs);
    }

    /**
     * 解析savedInstanceState，以得到保存的Fragments。
     * 在onCreate(OnCreateView等)时调用
     *
     * @param parent             {@link FragmentParent}
     * @param savedInstanceState {@link Bundle}
     * @see FragmentFactory#onSaveInstanceState(FragmentParent, Bundle)
     */
    @SuppressWarnings("unchecked")
    public static void parseSavedInstanceState(FragmentParent parent, Bundle savedInstanceState) {
        if (savedInstanceState == null || savedInstanceState.isEmpty()) return;
        Serializable fs = savedInstanceState.getSerializable("OriginActivityFragment_FF");
        if (fs != null) {
            parent.setFragments((HashMap<String, Object>) fs);
        }
    }

    /**
     * FragmentParent在各种情况下获取Fragment，设置Fragment
     *
     * @see king.dominic.jlibrary.activity.OriginActivity
     * @see king.dominic.jlibrary.fragment.OriginFragment
     * @see FragmentSaver
     */
    public interface FragmentParent {
        /**
         * 获取保存的Fragments
         *
         * @see FragmentFactory#onSaveInstanceState(FragmentParent, Bundle)
         */
        HashMap<String, Object> getFragments();

        /**
         * 设置Fragments以供保存
         *
         * @see FragmentFactory#onSaveInstanceState(FragmentParent, Bundle)
         */
        void setFragments(HashMap<String, Object> fs);

        /**
         * 获取FragmentManager，在Fragment中为{@link Fragment#getChildFragmentManager()}，
         * 在Activity中为{@link FragmentActivity#getSupportFragmentManager()}
         *
         * @return {@link FragmentManager}
         */
        FragmentManager gFragmentManager();
    }

    /**
     * 保存Fragment，提供给FragmentParent获取
     * 在Activity和Fragment中必须调用{@link #rescue(Bundle)}和{@link #save(Bundle)}
     *
     * @see king.dominic.jlibrary.fragment.OriginFragment
     */
    public static class FragmentSaver {
        private final FragmentParent parent;
        private HashMap<String, Object> mf;

        public FragmentSaver(FragmentParent parent) {
            this.parent = parent;
        }

        /**
         * @see FragmentParent#setFragments(HashMap)
         */
        public void set(HashMap<String, Object> f) {
            if (mf == f) {
                return;
            }
            mf = f;
        }

        /**
         * @see FragmentParent#getFragments()
         */
        public HashMap<String, Object> get() {
            return mf;
        }

        /**
         * 保存Fragments。页面onCreate(OnCreateView等)时调用此方法
         *
         * @param outState {@link Bundle}
         * @see FragmentFactory#onSaveInstanceState(FragmentParent, Bundle)
         */
        public void save(@NonNull Bundle outState) {
            onSaveInstanceState(parent, outState);
        }

        /**
         * 获取Fragments。页面onSaveInstanceState时调用此方法
         *
         * @param savedInstanceState {@link Bundle}
         * @see FragmentFactory#parseSavedInstanceState(FragmentParent, Bundle)
         */
        public void rescue(@Nullable Bundle savedInstanceState) {
            parseSavedInstanceState(parent, savedInstanceState);
        }

        /**
         * 获取FragmentParent的FragmentManager
         *
         * @return {@link FragmentManager}
         */
        public FragmentManager fm() {
            if (parent instanceof FragmentActivity)
                return ((FragmentActivity) parent).getSupportFragmentManager();
            if (parent instanceof Fragment)
                return ((Fragment) parent).getChildFragmentManager();
            return null;
        }

        @Override
        public String toString() {
            if (mf != null) {
                return mf.toString();
            }
            return super.toString();
        }
    }
}
